/**
 * // Copyright (c) 2023 KylinOS. All rights reserved.
 * //
 * // This program is free software; you can redistribute it
 * // and/or modify it under the terms of the GNU General Public
 * // License (version 2) as published by the FSF - Free Software
 * // Foundation*
 */

/**
 * // Copyright (C) 2023 ben LG <guoliben@kylinos.cn>
 */

package compressextractor

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"encoding/binary"
	"io"
	"os"
	"strings"
	"time"

	"gitee.com/guoliben/kydlp-doctotext/3rdparty/fileidentify"
	"gitee.com/guoliben/kydlp-doctotext/compressextractor/fileattrfilter"
	"gitee.com/guoliben/kydlp-doctotext/docextractor"
	"gitee.com/guoliben/kydlp-doctotext/extractor_interface"
	"gitee.com/guoliben/kydlp-doctotext/imageextractor"
	"gitee.com/guoliben/kydlp-doctotext/utils/ctrlledprint"
)

type CompressGz struct{}

func (extractor CompressGz) GetTextFromFile(path string, size int64, filetype string, depth int) ([]byte, string) {
	ctrlledprint.Println("提取文本: 正在解析gz(file)文档...")

	return gzGetTextFromFile(path, size, filetype, depth)
}

func (extractor CompressGz) GetTextFromBuffer(path string, data []byte, size int64, filetype string, depth int) ([]byte, string) {
	ctrlledprint.Println("提取文本: 正在解析gz(buffer)文档...")

	return gzGetTextFromBuffer(path, data, size, filetype, depth)
}

func (extractor CompressGz) GetStringTextFromFile(path string, size int64, filetype string, depth int) (string, string) {
	ctrlledprint.Println("提取文本: 正在解析gz(file)文档...")

	return gzGetStringTextFromFile(path, size, filetype, depth)
}

func (extractor CompressGz) GetStringTextFromBuffer(path string, data []byte, size int64, filetype string, depth int) (string, string) {
	ctrlledprint.Println("提取文本: 正在解析gz(buffer)文档...")

	return gzGetStringTextFromBuffer(path, data, size, filetype, depth)
}

func (extractor CompressGz) GetTextFromFileWithCallback(path string, size int64, filetype string, depth int, callback extractor_interface.OnTextDetectHandler, context interface{}) (int, []byte, string) {
	ctrlledprint.Println("提取文本: 正在解析gz(file)文档...")

	return gzGetTextFromFileWithCallback(path, size, filetype, depth, callback, context)
}

func (extractor CompressGz) GetTextFromBufferWithCallback(path string, data []byte, size int64, filetype string, depth int, callback extractor_interface.OnTextDetectHandler, context interface{}) (int, []byte, string) {
	ctrlledprint.Println("提取文本: 正在解析gz(buffer)文档...")

	return gzGetTextFromBufferWithCallback(path, data, size, filetype, depth, callback, context)
}

func (extractor CompressGz) GetStringTextFromFileWithCallback(path string, size int64, filetype string, depth int, callback extractor_interface.OnStringTextDetectHandler, context interface{}) (int, string, string) {
	ctrlledprint.Println("提取文本: 正在解析gz(file)文档...")

	return gzGetStringTextFromFileWithCallback(path, size, filetype, depth, callback, context)
}

func (extractor CompressGz) GetStringTextFromBufferWithCallback(path string, data []byte, size int64, filetype string, depth int, callback extractor_interface.OnStringTextDetectHandler, context interface{}) (int, string, string) {
	ctrlledprint.Println("提取文本: 正在解析gz(buffer)文档...")

	return gzGetStringTextFromBufferWithCallback(path, data, size, filetype, depth, callback, context)
}

//TODO: ~ 应4.4场景需求，需要捕获每种错误
func gzGetTextFromFile(path string, size int64, filetype string, depth int) ([]byte, string) {
	if depth > maxDetectDepth {
		ctrlledprint.Println("解析gz文档，达到最大解压层数:", maxDetectDepth)
		return nil, ""
	}

	start := time.Now()

	file, err := os.Open(path)
	if err != nil {
		ctrlledprint.Println("Error opening gz file:", err)
		return nil, ""
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil || stat.Size() <= 0 {
		ctrlledprint.Println("Get gz file size failed:", err)
		return nil, ""
	}
	gzSize := gzGetOrigSizeByPath(path)
	if uint32(stat.Size()) >= MAX_COMPRESS_LIMIT || gzSize >= MAX_COMPRESS_LIMIT {
		ctrlledprint.Println(path, "Gz file is larger then limit", MAX_COMPRESS_LIMIT)
		return nil, ""
	}

	// 创建一个 gzip 读取器
	gz, err := gzip.NewReader(file)
	if err != nil {
		ctrlledprint.Println("Error creating gz reader:", err)
		return nil, ""
	}
	defer gz.Close()

	// 读取并解压缩数据
	content, _, err := gzReadAll(gz, int64(gzSize))
	if err != nil {
		ctrlledprint.Println("[gz] Error reading content of sub file:", err)
		return nil, ""
	}
	length := int64(len(content))

	var text bytes.Buffer

	// 调用指定类型文件解析器解析文件内容
	// -------------------------------------------------------------------------------------------------------
	filetype, _, _, _ = fileidentify.IdentifyFromBuffer(content)

	switch strings.TrimSpace(filetype) {
	case fileidentify.DocTypeText, fileidentify.DocTypeHtml, fileidentify.DocTypeXml, fileidentify.DocTypeJson,
		fileidentify.DocTypePdf,
		fileidentify.DocTypeMsDoc, fileidentify.DocTypeMsDocx,
		fileidentify.DocTypeMsXls, fileidentify.DocTypeMsXlsx,
		fileidentify.DocTypeMsPpt, fileidentify.DocTypeMsPptx:
		{
			if result, _ := docextractor.TextExtractorFactory(filetype).GetTextFromBuffer(path, content, length, filetype, depth); len(result) > 0 {
				text.Write(result)
			}
		}
	case fileidentify.CompressTypeTar, fileidentify.CompressTypeGzOrTarGz, fileidentify.CompressTypeZip,
		fileidentify.CompressTypeRar, fileidentify.CompressTypeRarAliase1, fileidentify.CompressTypeRarAliase2,
		fileidentify.CompressType7z:
		{
			if result, _ := TextExtractorFactory(filetype).GetTextFromBuffer(path, content, length, filetype, depth + 1); len(result) > 0 {
				text.Write(result)
			}
		}
	case fileidentify.ImageTypeBmp, fileidentify.ImageTypeJpeg, fileidentify.ImageTypePng,
		fileidentify.ImageTypeGif, fileidentify.ImageTypeTiff:
		{
			if result, _ := imageextractor.TextExtractorFactory(filetype).GetTextFromBuffer(path, content, length, filetype, depth); len(result) > 0 {
				text.Write(result)
			}
		}
	}
	// -------------------------------------------------------------------------------------------------------

	elapsed := time.Since(start)

	return text.Bytes(), elapsed.String()
}

func gzGetTextFromBuffer(path string, data []byte, size int64, filetype string, depth int) ([]byte, string) {
	if depth > maxDetectDepth {
		ctrlledprint.Println("解析gz文档，达到最大解压层数:", maxDetectDepth)
		return nil, ""
	}

	start := time.Now()

	gzSize := gzGetOrigSizeByBuffer(data)
	if uint32(len(data)) >= MAX_COMPRESS_LIMIT || gzSize >= MAX_COMPRESS_LIMIT {
		ctrlledprint.Println(path, "Gz file is larger then limit", MAX_COMPRESS_LIMIT)
		return nil, ""
	}

	gz, err := gzip.NewReader(bytes.NewReader(data))
	if err != nil {
		ctrlledprint.Println("Error creating gz reader:", err)
		return nil, ""
	}
	defer gz.Close()

	content, _, err := gzReadAll(gz, int64(gzSize))
	if err != nil {
		ctrlledprint.Println("[gz] Error reading content of sub file:", err)
		return nil, ""
	}
	length := int64(len(content))

	var text bytes.Buffer

	// 调用指定类型文件解析器解析文件内容
	// -------------------------------------------------------------------------------------------------------
	filetype, _, _, _ = fileidentify.IdentifyFromBuffer(content)

	switch strings.TrimSpace(filetype) {
	case fileidentify.DocTypeText, fileidentify.DocTypeHtml, fileidentify.DocTypeXml, fileidentify.DocTypeJson,
		fileidentify.DocTypePdf,
		fileidentify.DocTypeMsDoc, fileidentify.DocTypeMsDocx,
		fileidentify.DocTypeMsXls, fileidentify.DocTypeMsXlsx,
		fileidentify.DocTypeMsPpt, fileidentify.DocTypeMsPptx:
		{
			if result, _ := docextractor.TextExtractorFactory(filetype).GetTextFromBuffer(path, content, length, filetype, depth); len(result) > 0 {
				text.Write(result)
			}
		}
	case fileidentify.CompressTypeTar, fileidentify.CompressTypeGzOrTarGz, fileidentify.CompressTypeZip,
		fileidentify.CompressTypeRar, fileidentify.CompressTypeRarAliase1, fileidentify.CompressTypeRarAliase2,
		fileidentify.CompressType7z:
		{
			if result, _ := TextExtractorFactory(filetype).GetTextFromBuffer(path, content, length, filetype, depth + 1); len(result) > 0 {
				text.Write(result)
			}
		}
	case fileidentify.ImageTypeBmp, fileidentify.ImageTypeJpeg, fileidentify.ImageTypePng,
		fileidentify.ImageTypeGif, fileidentify.ImageTypeTiff:
		{
			if result, _ := imageextractor.TextExtractorFactory(filetype).GetTextFromBuffer(path, content, length, filetype, depth); len(result) > 0 {
				text.Write(result)
			}
		}
	}
	// -------------------------------------------------------------------------------------------------------

	elapsed := time.Since(start)

	return text.Bytes(), elapsed.String()
}

func gzGetStringTextFromFile(path string, size int64, filetype string, depth int) (string, string) {
	if depth > maxDetectDepth {
		ctrlledprint.Println("解析gz文档，达到最大解压层数:", maxDetectDepth)
		return "", ""
	}

	start := time.Now()

	file, err := os.Open(path)
	if err != nil {
		ctrlledprint.Println("Error opening gz file:", err)
		return "", ""
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil || stat.Size() <= 0 {
		ctrlledprint.Println("Get gz file size failed:", err)
		return "", ""
	}
	gzSize := gzGetOrigSizeByPath(path)
	if uint32(stat.Size()) >= MAX_COMPRESS_LIMIT || gzSize >= MAX_COMPRESS_LIMIT {
		ctrlledprint.Println(path, "Gz file is larger then limit", MAX_COMPRESS_LIMIT)
		return "", ""
	}

	// 创建一个 gzip 读取器
	gz, err := gzip.NewReader(file)
	if err != nil {
		ctrlledprint.Println("Error creating gz reader:", err)
		return "", ""
	}
	defer gz.Close()

	// 读取并解压缩数据
	content, _, err := gzReadAll(gz, int64(gzSize))
	if err != nil {
		ctrlledprint.Println("[gz] Error reading content of sub file:", err)
		return "", ""
	}
	length := int64(len(content))

	var text bytes.Buffer

	// 调用指定类型文件解析器解析文件内容
	// -------------------------------------------------------------------------------------------------------
	filetype, _, _, _ = fileidentify.IdentifyFromBuffer(content)

	switch strings.TrimSpace(filetype) {
	case fileidentify.DocTypeText, fileidentify.DocTypeHtml, fileidentify.DocTypeXml, fileidentify.DocTypeJson,
		fileidentify.DocTypePdf,
		fileidentify.DocTypeMsDoc, fileidentify.DocTypeMsDocx,
		fileidentify.DocTypeMsXls, fileidentify.DocTypeMsXlsx,
		fileidentify.DocTypeMsPpt, fileidentify.DocTypeMsPptx:
		{
			if result, _ := docextractor.TextExtractorFactory(filetype).GetStringTextFromBuffer(path, content, length, filetype, depth); len(result) > 0 {
				text.WriteString(result)
			}
		}
	case fileidentify.CompressTypeTar, fileidentify.CompressTypeGzOrTarGz, fileidentify.CompressTypeZip,
		fileidentify.CompressTypeRar, fileidentify.CompressTypeRarAliase1, fileidentify.CompressTypeRarAliase2,
		fileidentify.CompressType7z:
		{
			if result, _ := TextExtractorFactory(filetype).GetStringTextFromBuffer(path, content, length, filetype, depth + 1); len(result) > 0 {
				text.WriteString(result)
			}
		}
	case fileidentify.ImageTypeBmp, fileidentify.ImageTypeJpeg, fileidentify.ImageTypePng,
		fileidentify.ImageTypeGif, fileidentify.ImageTypeTiff:
		{
			if result, _ := imageextractor.TextExtractorFactory(filetype).GetStringTextFromBuffer(path, content, length, filetype, depth); len(result) > 0 {
				text.WriteString(result)
			}
		}
	}
	// -------------------------------------------------------------------------------------------------------

	elapsed := time.Since(start)

	return text.String(), elapsed.String()
}

func gzGetStringTextFromBuffer(path string, data []byte, size int64, filetype string, depth int) (string, string) {
	if depth > maxDetectDepth {
		ctrlledprint.Println("解析gz文档，达到最大解压层数:", maxDetectDepth)
		return "", ""
	}

	start := time.Now()

	gzSize := gzGetOrigSizeByBuffer(data)
	if uint32(len(data)) >= MAX_COMPRESS_LIMIT || gzSize >= MAX_COMPRESS_LIMIT {
		ctrlledprint.Println(path, "Gz file is larger then limit", MAX_COMPRESS_LIMIT)
		return "", ""
	}

	gz, err := gzip.NewReader(bytes.NewReader(data))
	if err != nil {
		ctrlledprint.Println("Error creating gz reader:", err)
		return "", ""
	}
	defer gz.Close()

	content, _, err := gzReadAll(gz, int64(gzSize))
	if err != nil {
		ctrlledprint.Println("[gz] Error reading content of sub file:", err)
		return "", ""
	}
	length := int64(len(content))

	var text bytes.Buffer

	// 调用指定类型文件解析器解析文件内容
	// -------------------------------------------------------------------------------------------------------
	filetype, _, _, _ = fileidentify.IdentifyFromBuffer(content)

	switch strings.TrimSpace(filetype) {
	case fileidentify.DocTypeText, fileidentify.DocTypeHtml, fileidentify.DocTypeXml, fileidentify.DocTypeJson,
		fileidentify.DocTypePdf,
		fileidentify.DocTypeMsDoc, fileidentify.DocTypeMsDocx,
		fileidentify.DocTypeMsXls, fileidentify.DocTypeMsXlsx,
		fileidentify.DocTypeMsPpt, fileidentify.DocTypeMsPptx:
		{
			if result, _ := docextractor.TextExtractorFactory(filetype).GetStringTextFromBuffer(path, content, length, filetype, depth); len(result) > 0 {
				text.WriteString(result)
			}
		}
	case fileidentify.CompressTypeTar, fileidentify.CompressTypeGzOrTarGz, fileidentify.CompressTypeZip,
		fileidentify.CompressTypeRar, fileidentify.CompressTypeRarAliase1, fileidentify.CompressTypeRarAliase2,
		fileidentify.CompressType7z:
		{
			if result, _ := TextExtractorFactory(filetype).GetStringTextFromBuffer(path, content, length, filetype, depth + 1); len(result) > 0 {
				text.WriteString(result)
			}
		}
	case fileidentify.ImageTypeBmp, fileidentify.ImageTypeJpeg, fileidentify.ImageTypePng,
		fileidentify.ImageTypeGif, fileidentify.ImageTypeTiff:
		{
			if result, _ := imageextractor.TextExtractorFactory(filetype).GetStringTextFromBuffer(path, content, length, filetype, depth); len(result) > 0 {
				text.WriteString(result)
			}
		}
	}
	// -------------------------------------------------------------------------------------------------------

	elapsed := time.Since(start)

	return text.String(), elapsed.String()
}

func gzGetTextFromFileWithCallback(path string, size int64, filetype string, depth int, callback extractor_interface.OnTextDetectHandler, context interface{}) (int, []byte, string) {
	if depth > maxDetectDepth {
		ctrlledprint.Println("解析gz文档，达到最大解压层数:", maxDetectDepth)
		return 0, nil, ""
	}

	file, err := os.Open(path)
	if err != nil {
		ctrlledprint.Println("Error opening gz file:", err)
		return 0, nil, ""
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil || stat.Size() <= 0 {
		ctrlledprint.Println("Get file size failed:", err)
		return 0, nil, ""
	}
	gzSize := gzGetOrigSizeByPath(path)
	if uint32(stat.Size()) >= MAX_COMPRESS_LIMIT || gzSize >= MAX_COMPRESS_LIMIT {
		ctrlledprint.Println(path, "File is larger then limit ", MAX_COMPRESS_LIMIT)
		return 0, nil, ""
	}

	// 创建一个 gzip 读取器
	gzReader, err := gzip.NewReader(file)
	if err != nil {
		ctrlledprint.Println("Error creating gz reader:", err)
		return 0, nil, ""
	}
	defer gzReader.Close()

	// 打开一个新的文件句柄，避免原文件句柄失效，用于创建tarReader
	file2, err := os.Open(path)
	if err != nil {
		ctrlledprint.Println("Error opening tar.gz file:", err)
		return 0, nil, ""
	}
	defer file2.Close()

	// 创建一个 gzip 读取器，用于.tar.gz类型解析
	tarReader, err := gzip.NewReader(file2)
	if err != nil {
		ctrlledprint.Println("Error creating tar.gz reader:", err)
		return 0, nil, ""
	}
	defer tarReader.Close()

	return tarGzGetText(path, gzReader, tarReader, stat.Size(), filetype, depth, callback, context)
}

func gzGetTextFromBufferWithCallback(path string, data []byte, size int64, filetype string, depth int, callback extractor_interface.OnTextDetectHandler, context interface{}) (int, []byte, string) {
	if depth > maxDetectDepth {
		ctrlledprint.Println("解析gz文档，达到最大解压层数:", maxDetectDepth)
		return 0, nil, ""
	}

	// 创建一个 gzip 读取器
	gzReader, err := gzip.NewReader(bytes.NewReader(data))
	if err != nil {
		ctrlledprint.Println("Error creating gz reader:", err)
		return 0, nil, ""
	}
	defer gzReader.Close()

	// 创建一个 gzip 读取器，用于.tar.gz类型解析
	tarReader, err := gzip.NewReader(bytes.NewReader(data))
	if err != nil {
		ctrlledprint.Println("Error creating tar.gz reader:", err)
		return 0, nil, ""
	}
	defer tarReader.Close()

	return tarGzGetText(path, gzReader, tarReader, int64(len(data)), filetype, depth, callback, context)
}

func gzGetStringTextFromFileWithCallback(path string, size int64, filetype string, depth int, callback extractor_interface.OnStringTextDetectHandler, context interface{}) (int, string, string) {
	if depth > maxDetectDepth {
		ctrlledprint.Println("解析gz文档，达到最大解压层数:", maxDetectDepth)
		return 0, "", ""
	}

	file, err := os.Open(path)
	if err != nil {
		ctrlledprint.Println("Error opening gz file:", err)
		return 0, "", ""
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil || stat.Size() <= 0 {
		ctrlledprint.Println("Get file size failed:", err)
		return 0, "", ""
	}
	gzSize := gzGetOrigSizeByPath(path)
	if uint32(stat.Size()) >= MAX_COMPRESS_LIMIT || gzSize >= MAX_COMPRESS_LIMIT {
		ctrlledprint.Println(path, "File is larger then limit ", MAX_COMPRESS_LIMIT)
		return 0, "", ""
	}

	// 创建一个 gzip 读取器
	gzReader, err := gzip.NewReader(file)
	if err != nil {
		ctrlledprint.Println("Error creating gz reader:", err)
		return 0, "", ""
	}
	defer gzReader.Close()

	// 打开一个新的文件句柄，避免原文件句柄失效，用于创建tarReader
	file2, err := os.Open(path)
	if err != nil {
		ctrlledprint.Println("Error opening tar.gz file:", err)
		return 0, "", ""
	}
	defer file2.Close()

	// 创建一个 gzip 读取器，用于.tar.gz类型解析
	tarReader, err := gzip.NewReader(file2)
	if err != nil {
		ctrlledprint.Println("Error creating tar.gz reader:", err)
		return 0, "", ""
	}
	defer tarReader.Close()

	return tarGzGetStringText(path, gzReader, tarReader, stat.Size(), filetype, depth, callback, context)
}

func gzGetStringTextFromBufferWithCallback(path string, data []byte, size int64, filetype string, depth int, callback extractor_interface.OnStringTextDetectHandler, context interface{}) (int, string, string) {
	if depth > maxDetectDepth {
		ctrlledprint.Println("解析gz文档，达到最大解压层数:", maxDetectDepth)
		return 0, "", ""
	}

	// 创建一个 gzip 读取器
	gzReader, err := gzip.NewReader(bytes.NewReader(data))
	if err != nil {
		ctrlledprint.Println("Error creating gz reader:", err)
		return 0, "", ""
	}
	defer gzReader.Close()

	// 创建一个 gzip 读取器，用于.tar.gz类型解析
	tarReader, err := gzip.NewReader(bytes.NewReader(data))
	if err != nil {
		ctrlledprint.Println("Error creating tar.gz reader:", err)
		return 0, "", ""
	}
	defer tarReader.Close()

	return tarGzGetStringText(path, gzReader, tarReader, int64(len(data)), filetype, depth, callback, context)
}

// tarGzGetText .tar.gz格式文件统一解析入口
func tarGzGetText(path string, gzReader *gzip.Reader, tarReader *gzip.Reader, size int64, filetype string, depth int, callback extractor_interface.OnTextDetectHandler, context interface{}) (int, []byte, string) {
	// gz压缩包中为tar格式文件，采用tar进行解析，否则采用gzip解析
	tarGzReader := tar.NewReader(tarReader)
	if header, result := TestTarReader(tarGzReader); result {
		ctrlledprint.Println(path, "是一个.tar.gz压缩文件!")
		return tarGetText(path, tarGzReader, header, size, filetype, depth, callback, context)
	} else {
		ctrlledprint.Println(path, "这是一个纯.gz压缩文件!")
		return singleGzGetText(path, gzReader, size, filetype, depth, callback, context)
	}
}

func tarGzGetStringText(path string, gzReader *gzip.Reader, tarReader *gzip.Reader, size int64, filetype string, depth int, callback extractor_interface.OnStringTextDetectHandler, context interface{}) (int, string, string) {
	// gz压缩包中为tar格式文件，采用tar进行解析，否则采用gzip解析
	tarGzReader := tar.NewReader(tarReader)
	if header, result := TestTarReader(tarGzReader); result {
		ctrlledprint.Println(path, "是一个.tar.gz压缩文件!")
		return tarGetStringText(path, tarGzReader, header, size, filetype, depth, callback, context)
	} else {
		ctrlledprint.Println(path, "这是一个纯.gz压缩文件!")
		return singleGzGetStringText(path, gzReader, size, filetype, depth, callback, context)
	}
}

// singleGzGetText .gz(非.tar.gz)格式文件统一解析入口
func singleGzGetText(path string, gzReader *gzip.Reader, size int64, filetype string, depth int, callback extractor_interface.OnTextDetectHandler, context interface{}) (int, []byte, string) {
	var retcode int
	var totaltext bytes.Buffer
	var totalelapsed time.Duration

	start := time.Now()

	subfilename := gzReader.Header.Name
	subfilesize := size
	if fileattrfilter.FilterExcludeFilepath(subfilename) {
		return 0, nil, ""
	}
	if fileattrfilter.FilterExcludeFilesize("", subfilesize) {
		return 0, nil, ""
	}

	gzSize := gzGetOrigSizeByPath(path)
	if uint32(size) >= MAX_COMPRESS_LIMIT || gzSize >= MAX_COMPRESS_LIMIT {
		ctrlledprint.Println(path, "Gz file is larger then limit", MAX_COMPRESS_LIMIT)
		return 0, nil, ""
	}

	// 读取并解压缩数据
	content, _, err := gzReadAll(gzReader, int64(gzSize))
	if err != nil {
		ctrlledprint.Println("[gz] Error reading content of sub file:", err)
		return 0, nil, ""
	}
	length := int64(len(content))

	// 调用指定类型文件解析器解析文件内容
	// -------------------------------------------------------------------------------------------------------
	filetype, extension, _, _ := fileidentify.IdentifyFromBuffer(content)
	if !fileattrfilter.FilterIncludeFiletype(filetype, extension) {
		return 0, nil, totalelapsed.String()
	}
	retcode, _, _ = RunTextExtractorFactory(path, content, length, filetype, depth, callback, context)
	// -------------------------------------------------------------------------------------------------------

	elapsed := time.Since(start)

	totalelapsed += elapsed

	return retcode, totaltext.Bytes(), totalelapsed.String()
}

func singleGzGetStringText(path string, gzReader *gzip.Reader, size int64, filetype string, depth int, callback extractor_interface.OnStringTextDetectHandler, context interface{}) (int, string, string) {
	var retcode int
	var totaltext bytes.Buffer
	var totalelapsed time.Duration

	start := time.Now()

	subfilename := gzReader.Header.Name
	subfilesize := size
	if fileattrfilter.FilterExcludeFilepath(subfilename) {
		return 0, "", ""
	}
	if fileattrfilter.FilterExcludeFilesize("", subfilesize) {
		return 0, "", ""
	}

	gzSize := gzGetOrigSizeByPath(path)
	if uint32(size) >= MAX_COMPRESS_LIMIT || gzSize >= MAX_COMPRESS_LIMIT {
		ctrlledprint.Println(path, "File is larger then limit ", MAX_COMPRESS_LIMIT)
		return 0, "", ""
	}

	// 读取并解压缩数据
	content, _, err := gzReadAll(gzReader, int64(gzSize))
	if err != nil {
		ctrlledprint.Println("[gz] Error reading content of sub file:", err)
		return 0, "", ""
	}
	length := int64(len(content))

	// 调用指定类型文件解析器解析文件内容
	// -------------------------------------------------------------------------------------------------------
	filetype, extension, _, _ := fileidentify.IdentifyFromBuffer(content)
	if !fileattrfilter.FilterIncludeFiletype(filetype, extension) {
		return 0, "", totalelapsed.String()
	}
	retcode, _, _ = RunStringTextExtractorFactory(path, content, length, filetype, depth, callback, context)
	// -------------------------------------------------------------------------------------------------------

	elapsed := time.Since(start)

	totalelapsed += elapsed

	return retcode, totaltext.String(), totalelapsed.String()
}

type sliceWriter struct {
	buf []byte
	n   int
}

func (w *sliceWriter) Write(p []byte) (int, error) {
	if w.n+len(p) > len(w.buf) {
		return 0, io.ErrShortBuffer
	}
	copy(w.buf[w.n:], p)
	w.n += len(p)
	return len(p), nil
}

func gzReadAll(reader *gzip.Reader, gzSize int64) ([]byte, int, error) {
	if gzSize > 0 {
		buf := make([]byte, gzSize) // 直接分配足够大
		w := &sliceWriter{buf: buf}
		n, err := io.CopyBuffer(w, reader, make([]byte, 32*1024))
		if err != nil && err != io.EOF {
			return nil, -1, err
		}
		return buf[:w.n], int(n), nil
	} else {
		var buf2 bytes.Buffer
		n, err := io.CopyBuffer(&buf2, reader, make([]byte, 32*1024))
		if err != nil {
			return nil, -1, err
		}
		return buf2.Bytes(), int(n), nil
	}
}

func gzGetOrigSizeByPath(path string) uint32 {
	var size uint32
	f, err := os.Open(path)
	if err != nil {
		return size
	}
	defer f.Close()
	fi, _ := f.Stat()
	gpSize := fi.Size()
	if gpSize < 8 {
		return uint32(gpSize)
	}
	// 读尾部 8 字节
	buf := make([]byte, 8)
	_, err = f.ReadAt(buf, gpSize-8)
	if err != nil {
		return size
	}
	isize := binary.LittleEndian.Uint32(buf[4:])
	return isize
}

func gzGetOrigSizeByBuffer(data []byte) uint32 {
	gzSize := len(data)
	if gzSize < 8 {
		return 0 // no original size means it's not a gz file
	}
	isize := binary.LittleEndian.Uint32(data[gzSize-4:])
	return isize
}
